/*
 A class to hold a Google Earth draggable polygon.
 */

// some color constants.
const COLOR_ORDER3D = new Array("red", "green", "blue", "yellow");
const HELP_LINE_FILL   = '00000000';
const HELP_LINE_STROKE = '66ff0000';

// the minimum default polygon radius when placing a ring of points.
const MIN_RADIUS_METERS = 10;

// epsilons for use in calculating if two lat-lon-alt points are equal.
const EPSILON = 0.000001

// when you center the pins on GE, this is the radius.
const CENTER_PINS_RADIUS_PERCENT = 0.1;
const HIT_TEST_ATTEMPTS = 100;

function Polygon3D() {
	this.points = new Array();
	this.triangles = new Array();
	this.fillStyle = '00000000'
	this.strokeStyle = '00000000'
	this.linewidthStyle = 0;
	this.buildInCenter();
}

function GETriangle( point0, point1, point2 ) {
	this.points = new Array(point0, point1, point2);
	this.placemark = ge.createPlacemark('');
	this.geometry = ge.createPolygon('');
	this.geometry.setAltitudeMode(ge.ALTITUDE_ABSOLUTE);
	this.placemark.setGeometry(this.geometry);
	this.updateEdges();
}

GETriangle.prototype.setStyle = function( fill, stroke, width ) {
	this.placemark.setStyleSelector(ge.createStyle(''));
	var lineStyle = this.placemark.getStyleSelector().getLineStyle();
	var polyStyle = this.placemark.getStyleSelector().getPolyStyle();
	lineStyle.getColor().set(stroke);
	lineStyle.setWidth(width);
	polyStyle.getColor().set(fill);
}

GETriangle.prototype.updateEdges = function() {
	var outer = ge.createLinearRing('');
	var p0 = this.points[0];
	var p1 = this.points[1];
	var p2 = this.points[2];
	outer.getCoordinates().pushLatLngAlt(p0.lat, p0.lon, p0.alt);
	outer.getCoordinates().pushLatLngAlt(p2.lat, p2.lon, p2.alt);
	outer.getCoordinates().pushLatLngAlt(p1.lat, p1.lon, p1.alt);
	this.geometry.setOuterBoundary(outer);
}

GETriangle.prototype.draw = function() {
	ge.getFeatures().appendChild(this.placemark);
}

GETriangle.prototype.undraw = function() {
	ge.getFeatures().removeChild(this.placemark);
}

// looks through the radio button form and sees if we're dealing with
// 1, 3, or 4 vertices.
Polygon3D.prototype.getVertices = function() {
	var vertices = 0;
	var type = "";
	for (i=0; i<document.corrForm.correspondence.length; i++) {
		if ( document.corrForm.correspondence[i].checked == true ) {
			type = document.corrForm.correspondence[i].value;
			break;
		}
	}
	if ( type != "" ) {
		vertices = parseInt(type);
	}
	return vertices;
}

// drops points underneath the camera.
Polygon3D.prototype.placePointsBelowCamera = function( camLat, camLon, camAlt ) {
	var alt = ge.getGlobe().getGroundAltitude(camLat, camLon);
	var theta = 0;
	var thetastep = 2 * PI / this.vertices;
	
	var radius = (camAlt - ge.getGlobe().getGroundAltitude(camLat, camLon)) / 4;
	radius = Math.max(radius, MIN_RADIUS_METERS);
	var latPerMeter = 0.00001 / distance(camLat,camLon,0,camLat + 0.00001,camLon,0);
	var lonPerMeter = 0.00001 / distance(camLat,camLon,0,camLat,camLon + 0.00001,0);
	for (var i = 0; i < this.vertices; i++){
		var lat = camLat + radius * latPerMeter * Math.cos(theta);
		var lon = camLon + radius * lonPerMeter * Math.sin(theta);
		
		this.addPoint(lat, lon, alt, COLOR_ORDER3D[i]);
		theta = theta + thetastep;
	}	
}

// places points in the center of the screen, intersecting with the
// google earth geometry, if available.
Polygon3D.prototype.centerPoints = function( camLat, camLon, camAlt ) {
	// drop a ring of point around the center.
	var theta = 0;
	var thetastep = 2 * PI / this.vertices;
	var hitTests = new Array();
	var anyHTNull = false;
	
	for ( var i = 0; i < this.vertices; i++ ) {
		var xPercent = 0.5 + CENTER_PINS_RADIUS_PERCENT * Math.cos(theta);
		var yPercent = 0.5 + CENTER_PINS_RADIUS_PERCENT * Math.sin(theta);

		var ht = absoluteHitTestPercent( xPercent, yPercent );

		if ( ht == null ) {
			anyHTNull = true;
			break;
		}

		hitTests[i] = ht;
		theta = theta + thetastep;
	}
	
	// if any were null (i.e., pointing toward the sky) don't place them
	// there.  Place them below the camera position.
	if ( anyHTNull ) {
		this.placePointsBelowCamera(camLat, camLon, camAlt);
	} else {
		for ( var i = 0; i < this.vertices; i++ ) {
			var ht = hitTests[i];
			this.addPoint(ht.lat, ht.lon, ht.alt, COLOR_ORDER3D[i]);
		}
	}
}

// clears out any old polygons, builds an n-gon in the center of the window,
// intersecting with the GE geometry.  If such an n-gon can't be built (i.e.,
// if the camera is looking at the horizon line and one of the points is in the sky)
//, this just drops a polygon underneath the camera.
Polygon3D.prototype.buildInCenter = function() {
	this.clear();
	this.points = new Array();
	this.triangles = new Array();
	
	this.vertices = this.getVertices();
	
	var cam = ge.getView().copyAsCamera(ge.ALTITUDE_ABSOLUTE);
	var camLat = cam.getLatitude();
	var camLon = cam.getLongitude();
	var camAlt = cam.getAltitude();
	
	if (this.vertices == 1) {
		// drop a point right in the center.
		var ht = absoluteHitTestPercent(0.5, 0.5);
		if (ht != null) {
			this.addPoint(ht.lat, ht.lon, ht.alt, COLOR_ORDER3D[0]);
		} else {
			this.addPoint(camLat, camLon, ge.getGlobe().getGroundAltitude(camLat,camLon), COLOR_ORDER3D[0]);
		}
	} else {
		this.centerPoints(camLat, camLon, camAlt);
	}
	
	if ( this.vertices == 3 ) {
		this.addTriangle(0, 1, 2);
	} else if ( this.vertices == 4 ) {
		this.addTriangle(0, 1, 2);
		this.addTriangle(0, 2, 3);
	}
	
	this.movedPoint = false;
	this.draw();
}

Polygon3D.prototype.draw = function() {
	// draw the points.
	for ( var i = 0; i < this.points.length; i++ ) {
		this.points[i].draw();
	}
	
	// draw the triangles.
	for ( var i = 0; i < this.triangles.length; i++ ) {
		this.triangles[i].draw();
	}
}

// add a point to the polygon.
Polygon3D.prototype.addPoint = function( lat, lon, alt, color ) {
	var p = new Point3D(lat, lon, alt, color, this);
	this.points[this.points.length] = p;
}

// add a triangle
Polygon3D.prototype.addTriangle = function( i0, i1, i2 ) {
	var p0 = this.points[i0];
	var p1 = this.points[i1];
	var p2 = this.points[i2];
	var tri = new GETriangle(p0, p1, p2);
	tri.setStyle(this.fillStyle,this.strokeStyle, this.linewidthStyle);
	this.triangles[this.triangles.length] = tri;
	p0.addParentTriangle(tri);
	p1.addParentTriangle(tri);
	p2.addParentTriangle(tri);
}

Polygon3D.prototype.setStyle = function( fill, stroke, width ) {
	this.fillStyle = fill;
	this.strokeStyle = stroke;
	this.linewidthStyle = width;
}

// clean out the old polygon from Google Earth.
Polygon3D.prototype.clear = function() {
	// undraw the points.
	for (var i = 0; i < this.points.length; i++ ) {
		this.points[i].undraw();
	}
	
	// undraw the triangles.
	for (var i = 0; i < this.triangles.length; i++ ) {
		var tri = this.triangles[i];
		for (var j = 0; j < 3; j++){
			tri.points[j].removeParents();
		}
		this.triangles[i].undraw();
	}
	
	delete points;
	delete triangles;
}

Polygon3D.prototype.movedPoints = function() {
	for ( var i = 0; i < this.points.length; i++ ) {
		if ( !this.points[i].movedPoint ) {
			return false;
		}
	}
	return true;
}

Polygon3D.prototype.allPointsHitBuilding = function() { 
	for ( var i = 0; i < this.points.length; i++ ) {
		if ( !this.points[i].hitBuilding ) {
			return false;
		}
	}
	return true;
}